package com.avocado.objectstore.service.impl;


import com.avocado.commons.utils.Base64Utils;
import com.avocado.commons.utils.DateUtils;
import com.avocado.objectstore.config.MinioConfiguration;
import com.avocado.objectstore.dto.file.FileContent;
import com.avocado.objectstore.dto.file.FileDto;
import com.avocado.objectstore.dto.file.UploadFileDto;
import com.avocado.objectstore.service.ObjectStoreService;
import com.avocado.objectstore.vo.file.FileVo;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


@Service
public class ObjectStoreServiceImpl implements ObjectStoreService{

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfiguration configuration;


    private static String DIRECTORY_FLAG="nothing_but_do_not_delete_it.txt";



    public List<String> upload(Long userId,UploadFileDto uploadFileDto) throws Exception{

        List<FileContent> fileContentList= uploadFileDto.getFileContentList();

        List<String> retList=new ArrayList<>(10);

        for(FileContent fileContent:fileContentList){

            InputStream inputStream=Base64Utils.base64ToInputStream(fileContent.getContent().split(",")[1]);

            String object=getUserFile(userId,uploadFileDto.getCurrentDirectory(),fileContent.getName());
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(configuration.getBucketName())
                    .object(object)
                    .stream(inputStream, inputStream.available(), -1)
                    //.contentType("application/octet-stream")
                            .contentType(fileContent.getContentType())
                    .build());

           String objectUrl= getPresignedObjectUrl(object);

           retList.add(objectUrl);
        }



        return retList;
    }

    public List<String> uploadMultipartFiles(Long userId,String currentDirectory, MultipartFile[] multipartFiles) throws Exception{

        List<String> retList=new ArrayList<>(10);

        for(MultipartFile multipartFile:multipartFiles){

            InputStream inputStream=multipartFile.getInputStream();

            String object=getUserFile(userId,currentDirectory,multipartFile.getOriginalFilename());

            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(configuration.getBucketName())
                    .object(object)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(multipartFile.getContentType())
                    .build());

            String objectUrl= getPresignedObjectUrl(object);

            retList.add(objectUrl);
        }

        return retList;
    }

    private String getPresignedObjectUrl(String object) throws Exception{
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(configuration.getBucketName()).method(Method.GET).object(object).build());
    }


    private String getUserFile(Long userId,String currentDirectory,String name){
        return  String.valueOf(userId).concat(File.separator).concat(currentDirectory).concat(name);
    }

    /**
     * 创建文件夹
     * 因为MINIO不能直接创建文件夹
     * 通过直接创件一个空文件来绕过这个BUG
     * @param userId
     * @param directoryPath
     * @return
     * @throws Exception
     */
    public void createDirectory(Long userId,String directoryPath)throws Exception{

        directoryPath=String.valueOf(userId).concat(File.separator).concat(directoryPath);

        if(!directoryPath.endsWith("/")){
            directoryPath=directoryPath.concat("/");
        }

        String object=directoryPath.concat(DIRECTORY_FLAG);
        minioClient.putObject(
                PutObjectArgs.builder().bucket(configuration.getBucketName()).object(object).stream(
                        new ByteArrayInputStream(new byte[] {}), 0, -1)
                        .build());



    }


    public void delete(Long userId, FileDto fileDto)throws Exception{

        for(String filePath:fileDto.getFilePaths()) {

            String object= String.valueOf(userId).concat(File.separator).concat(filePath);

            removeObject(object);
        }


    }

    private void removeObject(String object) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException {
        if (object.endsWith("/")) {
            removeDirectory(object);
        } else {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(configuration.getBucketName()).object(object).build());
        }
    }

    private void removeDirectory(String object){
        Iterable<Result<Item>> list = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(configuration.getBucketName()).prefix(object).recursive(false).build());
        list.forEach(item -> {
            try {
                String objectName=item.get().objectName();
                if(objectName.endsWith("/")){
                    removeDirectory(objectName);
                }else {
                    minioClient.removeObject(RemoveObjectArgs.builder().bucket(configuration.getBucketName()).object(objectName).build());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public List<FileVo> list(Long userId,String currentDirectory) throws Exception{

        String prefix=String.valueOf(userId).concat(File.separator).concat(currentDirectory);

        if(!StringUtils.isEmpty(currentDirectory)&&!currentDirectory.endsWith("/")){
            prefix=prefix.concat(File.separator);
        }

        Iterable<Result<Item>> objectIterable=minioClient.listObjects(
                ListObjectsArgs.builder().bucket(configuration.getBucketName()).prefix(prefix).recursive(false).build());

        Iterator iterator= objectIterable.iterator();

        List<FileVo> fileVoList=new ArrayList<>();

        while (iterator.hasNext()){

            Result<Item> result=(Result<Item>)iterator.next();

            Item item= result.get();

            //这里objectName就是完整的路径
            String objectName =item.objectName();

            String lastName =getLastName(objectName);

            String lastModified="";
            //此处有个BUG ，当item是文件夹时 item.lastModified()会报空指针异常
            if(!item.isDir()&&item.lastModified()!=null){
                lastModified=item.lastModified().format(DateTimeFormatter.ofPattern(DateUtils.FORMAT_DATE_TIME_DEFAULT));
            }

            String key=objectName.replaceFirst(String.valueOf(userId)+"/","");

            FileVo fileVo=new FileVo(lastName,currentDirectory,item.isDir(),item.size(),"",lastModified,key);

            if(!item.isDir()){
              String url= getPresignedObjectUrl(item.objectName());
              fileVo.setUrl(url);
            }
            //排除掉目录标志，以免用户删掉
            if(!fileVo.getName().equals(DIRECTORY_FLAG)){
                fileVoList.add(fileVo);
            }
        }
        return fileVoList;
    }

    private String getLastName(String objectName){
        //如果是文件夹则去除最后一个/ 以便获取文件夹名称
        if(objectName.endsWith("/")){
            objectName=objectName.substring(0,objectName.length()-1);
        }

        String lastName=objectName.substring(objectName.lastIndexOf("/")+1,objectName.length());

        return lastName;
    }

    public void rename(Long userId,String oldFilePath,String newFilePath)throws Exception{

        String sourceObject=String.valueOf(userId).concat(File.separator).concat(oldFilePath);

        String object=String.valueOf(userId).concat(File.separator).concat(newFilePath);

        if(sourceObject.endsWith("/")){
            renameDirectory(sourceObject,object);
        }else {
            copyObject(sourceObject, object);
        }
        removeObject(sourceObject);


    }

    private void copyObject(String sourceObject, String object) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException {
        minioClient.copyObject(
                CopyObjectArgs.builder()
                        .bucket(configuration.getBucketName())
                        .object(object)
                        .source(
                                CopySource.builder()
                                        .bucket(configuration.getBucketName())
                                        .object(sourceObject)
                                        .build())
                        .build());
    }


    private void renameDirectory(String sourceObject,String targetObject){
        Iterable<Result<Item>> list = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(configuration.getBucketName()).prefix(sourceObject).recursive(false).build());
        list.forEach(item -> {
            try {
                String sourceItemObject=item.get().objectName();
                String lastName =getLastName(sourceItemObject);
                String targetItemObject=targetObject.concat(lastName);
                if(sourceItemObject.endsWith("/")){
                    targetItemObject=targetItemObject.concat("/");
                    renameDirectory(sourceItemObject,targetItemObject);
                }else {
                    copyObject(sourceItemObject,targetItemObject);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }




}
